#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
using  namespace std;

const int gmaxcap=5;

template<class T>
class BlockQueue
{  
private:
    std::queue<T> q_;
    int maxcap_;//队列容量
    pthread_mutex_t mutex_;
    pthread_cond_t  pcond_;//生产者对应的条件变量
    pthread_cond_t  ccond_;//消费者者对应的条件变量
public:
    BlockQueue(const int& maxcap=gmaxcap)
    :maxcap_(maxcap)
    {
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&pcond_,nullptr);
        pthread_cond_init(&ccond_,nullptr);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&pcond_);
        pthread_cond_destroy(&ccond_);
    }

    void push(const T &in)//输入性参数 &
    {
        pthread_mutex_lock(&mutex_);

        //1.判断
        //细节2:充当条件变量的语法必须是while,不能是if
        while(is_full())
        {   
            //细节1:该函数会以原子性的方式将锁释放，并将自己挂起
            //被唤醒的时候会自动获取传入的锁
            pthread_cond_wait(&pcond_,&mutex_);//缓冲区满，生产者阻塞等待
        }
        //2.这一步一定没有满
        q_.push(in);
        //3.堵塞队列一定有数据

        //细节3：唤醒行为可以放在解锁前也可以放在解锁后
        pthread_cond_signal(&ccond_);//唤醒消费者
        pthread_mutex_unlock(&mutex_);
        //pthread_cond_signal(&ccond_);//唤醒消费者

    }
    void pop(T* out)//输出型参数：*
    {
        pthread_mutex_lock(&mutex_);
        //1.判断
        while(is_empty())
        {
            pthread_cond_wait(&ccond_,&mutex_);//缓冲区空，消费者阻塞等待
        }
        //2.这一步一定没有满
        *out = q_.front();
        q_.pop();

        //3.堵塞队列一定没有满
        pthread_cond_signal(&pcond_);//唤醒生产者
        pthread_mutex_unlock(&mutex_);
    }
private:
    bool is_empty()
    {
        return q_.empty();
    }    
    bool is_full()
    {
        return q_.size()==maxcap_;
    } 
};
